/**
 * Implementing Drag and Drop functionality in AngularJS is easier than ever.
 * Demo: http://codef0rmer.github.com/angular-dragdrop/
 *
 * @version 1.0.3
 *
 * (c) 2013 Amit Gharat a.k.a codef0rmer <amit.2006.it@gmail.com> - amitgharat.wordpress.com
 */
var jqyoui = angular.module("ngDragDrop", []).service("ngDragDropService", ["$timeout", "$parse", function ($timeout, $parse) {
    this.callEventCallback = function (scope, callbackName, event, ui) {
        if (!callbackName) {
            return
        }
        var args = [event, ui];
        var match = callbackName.match(/^(.+)\((.+)\)$/);
        if (match !== null) {
            callbackName = match[1];
            values = eval("[" + match[0].replace(/^(.+)\(/, "").replace(/\)/, "") + "]");
            args.push.apply(args, values)
        }
        scope[callbackName].apply(scope, args)
    };
    this.invokeDrop = function (e, t, n, r) {
        var i = "", s = "", o = {}, u = {}, a = null, f = {}, l = {}, c, h, p = null, d = t.scope(), v = e.scope();
        i = e.attr("ng-model");
        s = t.attr("ng-model");
        c = v.$eval(i);
        h = d.$eval(s);
        p = t.find("[jqyoui-draggable]:last");
        u = d.$eval(t.attr("jqyoui-droppable")) || [];
        o = v.$eval(e.attr("jqyoui-draggable")) || [];
        o.index = this.fixIndex(v, o, c);
        u.index = this.fixIndex(d, u, h);
        a = angular.isArray(c) ? o.index : null;
        f = angular.isArray(c) ? c[a] : c;
        if (angular.isArray(h) && u && u.index !== undefined) {
            l = h[u.index]
        } else if (!angular.isArray(h)) {
            l = h
        } else {
            l = {}
        }
        if (o.animate === true) {
            this.move(e, p.length > 0 ? p : t, null, "fast", u, null);
            this.move(p.length > 0 && !u.multiple ? p : [], e.parent("[jqyoui-droppable]"), jqyoui.startXY, "fast", u, function () {
                $timeout(function () {
                    e.css({position: "relative", left: "", top: ""});
                    p.css({position: "relative", left: "", top: ""});
                    this.mutateDraggable(v, u, o, i, s, l, e);
                    this.mutateDroppable(d, u, o, s, f, a);
                    this.callEventCallback(d, u.onDrop, n, r)
                }.bind(this))
            }.bind(this))
        } else {
            $timeout(function () {
                this.mutateDraggable(v, u, o, i, s, l, e);
                this.mutateDroppable(d, u, o, s, f, a);
                this.callEventCallback(d, u.onDrop, n, r)
            }.bind(this))
        }
    };
    this.move = function (e, t, n, r, i, s) {
        if (e.length === 0) {
            if (s) {
                window.setTimeout(function () {
                    s()
                }, 300)
            }
            return false
        }
        var o = 9999, u = e.offset(), a = t && t.is(":visible");
        if (n === null && t.length > 0) {
            if (t.attr("jqyoui-draggable") !== undefined && t.attr("ng-model") !== undefined && t.is(":visible") && i && i.multiple) {
                n = t.offset();
                if (i.stack === false) {
                    n.left += t.outerWidth(true)
                } else {
                    n.top += t.outerHeight(true)
                }
            } else {
                n = t.css({visibility: "hidden", display: "block"}).offset();
                t.css({visibility: "", display: a ? "" : "none"})
            }
        }
        e.css({position: "absolute", "z-index": o}).css(u).animate(n, r, function () {
            if (s)s()
        })
    };
    this.mutateDroppable = function (e, t, n, r, i, s) {
        var o = e.$eval(r);
        e.__dragItem = i;
        if (angular.isArray(o)) {
            if (t && t.index >= 0) {
                o[t.index] = i
            } else {
                o.push(i)
            }
            if (n && n.placeholder === true) {
                o[o.length - 1]["jqyoui_pos"] = s
            }
        } else {
            $parse(r + " = __dragItem")(e);
            if (n && n.placeholder === true) {
                o["jqyoui_pos"] = s
            }
        }
    };
    this.mutateDraggable = function (e, t, n, r, i, s, o) {
        var u = angular.equals(angular.copy(s), {}), a = e.$eval(r);
        e.__dropItem = s;
        if (n && n.placeholder) {
            if (n.placeholder != "keep") {
                if (angular.isArray(a) && n.index !== undefined) {
                    a[n.index] = s
                } else {
                    $parse(r + " = __dropItem")(e)
                }
            }
        } else {
            if (angular.isArray(a)) {
                if (u) {
                    if (n && n.placeholder !== true && n.placeholder !== "keep") {
                        a.splice(n.index, 1)
                    }
                } else {
                    a[n.index] = s
                }
            } else {
                $parse(r + " = __dropItem")(e);
                if (e.$parent) {
                    $parse(r + " = __dropItem")(e.$parent)
                }
            }
        }
        o.css({"z-index": "", left: "", top: ""})
    };
    this.fixIndex = function (e, t, n) {
        if (t.applyFilter && angular.isArray(n) && n.length > 0) {
            var r = e[t.applyFilter](), i = r[t.index], s = undefined;
            n.forEach(function (e, t) {
                if (angular.equals(e, i)) {
                    s = t
                }
            });
            return s
        }
        return t.index
    }
}]).directive("jqyouiDraggable", ["ngDragDropService", function (e) {
    return {
        require: "?jqyouiDroppable", restrict: "A", link: function (t, n, r) {
            var i, s;
            var o = function (o, u) {
                if (o) {
                    i = t.$eval(n.attr("jqyoui-draggable")) || [];
                    n.draggable({disabled: false}).draggable(t.$eval(r.jqyouiOptions) || {}).draggable({
                        start: function (n, r) {
                            s = angular.element(this).css("z-index");
                            angular.element(this).css("z-index", 99999);
                            jqyoui.startXY = angular.element(this).offset();
                            e.callEventCallback(t, i.onStart, n, r)
                        }, stop: function (n, r) {
                            angular.element(this).css("z-index", s);
                            e.callEventCallback(t, i.onStop, n, r)
                        }, drag: function (n, r) {
                            e.callEventCallback(t, i.onDrag, n, r)
                        }
                    })
                } else {
                    n.draggable({disabled: true})
                }
            };
            t.$watch(function () {
                return t.$eval(r.drag)
            }, o);
            o()
        }
    }
}]).directive("jqyouiDroppable", ["ngDragDropService", function (e) {
    return {
        restrict: "A", priority: 1, link: function (t, n, r) {
            var i = function (i, s) {
                if (i) {
                    n.droppable({disabled: false}).droppable(t.$eval(r.jqyouiOptions) || {}).droppable({
                        over: function (n, r) {
                            var i = t.$eval(angular.element(this).attr("jqyoui-droppable")) || [];
                            e.callEventCallback(t, i.onOver, n, r)
                        }, out: function (n, r) {
                            var i = t.$eval(angular.element(this).attr("jqyoui-droppable")) || [];
                            e.callEventCallback(t, i.onOut, n, r)
                        }, drop: function (t, n) {
                            e.invokeDrop(angular.element(n.draggable), angular.element(this), t, n)
                        }
                    })
                } else {
                    n.droppable({disabled: true})
                }
            };
            t.$watch(function () {
                return t.$eval(r.drop)
            }, i);
            i()
        }
    }
}])
